#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include "read_write_file_range.h"
#include "http_server.h"

#define READ_BLOCK (10 * 1024)

int read_write_file_range (http_connection_t *http_connection, off_t to)
{
	/* Return values:
	 * -1: Error.
	 *  0: Send has been interrupted.
	 *  1: Range has been sent.
	 */

	buffer_t *buffer;
	size_t length;
	size_t sent;
	ssize_t bytes;
	size_t zero_bytes_sent;

	buffer = &http_connection->buffer;

	do {
		length = to - http_connection->offset + 1;
		if (length == 0) {
			return 1;
		}

		if (length > READ_BLOCK) {
			length = READ_BLOCK;
		}

		buffer_reset (buffer);
		if (buffer_allocate (buffer, length) < 0) {
			return -1;
		}

		if (lseek (http_connection->fd, http_connection->offset, SEEK_SET) != http_connection->offset) {
			return -1;
		}

		/* Read from file. */
		bytes = read (http_connection->fd, buffer->data, length);
		if (bytes <= 0) {
			return 1;
		}

		length = bytes;
		sent = 0;
		zero_bytes_sent = 0;

		/* Send chunk read. */
		do {
			bytes = send (http_connection->sd, buffer->data + sent, length - sent, 0);
			if (bytes < 0) {
				if (errno == EAGAIN) {
					return 0;
				} else if (errno == EINTR) {
					/* A signal occurred before any data was transmitted. */
					/* Add ourself to the list of interrupted HTTP connections. */
					http_server.interrupted_http_connections[http_server.number_interrupted_http_connections++] = http_connection;

					return 0;
				} else {
					return -1;
				}
			} else if (bytes == 0) {
				if (++zero_bytes_sent >= 5) {
					return -1;
				}
			} else {
				http_connection->last_read_write = http_server.current_time;

				http_connection->sent += bytes;
				http_connection->offset += bytes;
				sent += bytes;

				zero_bytes_sent = 0;

				if (sent == length) {
					break;
				}
			}
		} while (1);
	} while (1);
}
